using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace _3D_Game
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class ModelManager : DrawableGameComponent
    {
        List<BasicModel> models = new List<BasicModel>();

        //Spawn variables
        Vector3 maxSpawnLocation = new Vector3(100, 100, -3000);
        int nextSpawnTime = 0;
        int timeSinceLastSpawn = 0;
        float maxRollAngle = MathHelper.Pi / 40;
        //Enemy count
        int enemiesThisLevel = 0;
        //Misses variables
        int missedThisLevel;
        //Current level
        int currentLevel = 0;

        //List of LevelInfo objects
        List<LevelInfo> levelInfoList = new List<LevelInfo>();
        List<BasicModel> shots = new List<BasicModel>();
        float shotMinZ = -3000;

        //explosions
        List<ParticleExplosion> explosions = new List<ParticleExplosion>();
        ParticleExplosionSettings particleExplisonSettings = new ParticleExplosionSettings();
        ParticleSettings particleSettings = new ParticleSettings();
        Texture2D explosionTexture;
        Texture2D explosionColorsTexture;
        Effect explosionEffect;

        //Stars
        ParticleStarSheet stars;
        Effect starEffect;
        Texture2D starTexture;

        public ModelManager(Game game)
            : base(game)
        {
            levelInfoList.Add(new LevelInfo(1000, 3000, 20, 2, 6, 10));
            levelInfoList.Add(new LevelInfo(900, 2800, 22, 2, 6, 9));
            levelInfoList.Add(new LevelInfo(800, 2600, 24, 2, 6, 8));
            levelInfoList.Add(new LevelInfo(700, 2400, 26, 3, 7, 7));
            levelInfoList.Add(new LevelInfo(600, 2200, 28, 3, 7, 6));
            levelInfoList.Add(new LevelInfo(500, 2000, 30, 3, 7, 5));
            levelInfoList.Add(new LevelInfo(400, 1800, 32, 4, 7, 4));
            levelInfoList.Add(new LevelInfo(300, 1600, 34, 4, 8, 3));
            levelInfoList.Add(new LevelInfo(200, 1400, 36, 5, 8, 2));
            levelInfoList.Add(new LevelInfo(100, 1200, 38, 5, 9, 1));
            levelInfoList.Add(new LevelInfo(50, 1000, 40, 6, 9, 0));
            levelInfoList.Add(new LevelInfo(50, 800, 42, 6, 9, 0));
            levelInfoList.Add(new LevelInfo(50, 600, 44, 8, 10, 0));
            levelInfoList.Add(new LevelInfo(25, 400, 46, 8, 10, 0));
            levelInfoList.Add(new LevelInfo(0, 200, 48, 18, 20, 0));


        }

        protected override void LoadContent()
        {
            explosionTexture = Game.Content.Load<Texture2D>("Textures/Particle");
            explosionColorsTexture = Game.Content.Load<Texture2D>("Textures/ParticleColors");
            explosionEffect = Game.Content.Load<Effect>("Effects/Particle");

            explosionEffect.CurrentTechnique = explosionEffect.Techniques["Technique1"];
            explosionEffect.Parameters["theTexture"].SetValue(explosionTexture);

            starTexture = Game.Content.Load<Texture2D>("Textures/Stars");
            starEffect = explosionEffect.Clone();
            starEffect.CurrentTechnique = starEffect.Techniques["Technique1"];
            starEffect.Parameters["theTexture"].SetValue(explosionTexture);

            stars = new ParticleStarSheet(GraphicsDevice, new Vector3(2000, 2000, -1900), 1500, particleSettings, starEffect, starTexture);

            base.LoadContent();
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            SetNextSpawnTime();
            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {

            CheckToSpawnEnemy(gameTime);
            UpdateModels();
            UpdateShots();
            UpdateExplosions(gameTime);
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            foreach(BasicModel bm in models)
            {
                bm.Draw(((Game1)Game).camera);
            }

            foreach (BasicModel bm in shots)
            {
                bm.Draw(((Game1)Game).camera);
            }

            foreach (ParticleExplosion pe in explosions)
            {
                pe.Draw(((Game1)Game).camera);
            }

            stars.Draw(((Game1)Game).camera);

            base.Draw(gameTime);
        }

        protected void CheckToSpawnEnemy(GameTime gametime)
        {
            if (enemiesThisLevel < levelInfoList[currentLevel].numberEnemies)
            {
                timeSinceLastSpawn += gametime.ElapsedGameTime.Milliseconds;
                if (timeSinceLastSpawn > nextSpawnTime)
                {
                    SpawnEnemy();
                }
            }
        }

        private void SetNextSpawnTime()
        {
            nextSpawnTime = ((Game1)Game).random.Next(levelInfoList[currentLevel].minSpawnTime, levelInfoList[currentLevel].maxSpawnTime);
            timeSinceLastSpawn = 0;
        }

        private void SpawnEnemy()
        {
            Vector3 position = new Vector3(((Game1)Game).random.Next(-(int)maxSpawnLocation.X, (int)maxSpawnLocation.X), ((Game1)Game).random.Next(-(int)maxSpawnLocation.Y, (int)maxSpawnLocation.Y), maxSpawnLocation.Z);

            Vector3 direction = new Vector3(0, 0, ((Game1)Game).random.Next(levelInfoList[currentLevel].minSpeed, levelInfoList[currentLevel].maxSpeed));

            float rollRotation = (float)((Game1)Game).random.NextDouble() * maxRollAngle - (maxRollAngle / 2);

            models.Add(new SpinningEnemy(Game.Content.Load<Model>("models/spaceship"), position, direction, 0, 0, rollRotation));

            ++enemiesThisLevel;
            SetNextSpawnTime();
        }

        protected void UpdateModels()
        {
            for (int i = 0; i < models.Count; ++i)
            {
                models[i].Update();

                //remove models that are out of bounds

                if (models[i].GetWorld().Translation.Z > ((Game1)Game).camera.cameraPosition.Z + 100)
                {
                    models.RemoveAt(i);
                    --i;
                }
            }
        }

        public void AddShot(Vector3 position, Vector3 direction)
        {
            shots.Add(new SpinningEnemy(Game.Content.Load<Model>("models/ammo"), position, direction, 0, 0, 0));
            shots.Add(new SpinningEnemy(Game.Content.Load<Model>("models/ammo"), position, new Vector3(direction.X + 2, direction.Y, direction.Z), 0, 0, 0));
            shots.Add(new SpinningEnemy(Game.Content.Load<Model>("models/ammo"), position, new Vector3(direction.X -2, direction.Y, direction.Z), 0, 0, 0));
        }

        protected void UpdateShots()
        {
            for (int i = 0; i < shots.Count; ++i)
            {
                shots[i].Update();

                if (shots[i].GetWorld().Translation.Z < shotMinZ)
                {
                    shots.RemoveAt(i);
                    --i;
                }
                else
                {
                    for (int j = 0; j<models.Count; ++j)
                    {
                        if(shots[i].CollidesWith(models[j].model, models[j].GetWorld()))
                        {
                            explosions.Add(new ParticleExplosion(GraphicsDevice, models[j].GetWorld().Translation, ((Game1)Game).random.Next(particleExplisonSettings.minLife, particleExplisonSettings.maxLife),
                                ((Game1)Game).random.Next(particleExplisonSettings.minRoundTime, particleExplisonSettings.maxRoundTime),
                                ((Game1)Game).random.Next(particleExplisonSettings.minParticlesPerRound, particleExplisonSettings.maxParticlesPerRound),
                                ((Game1)Game).random.Next(particleExplisonSettings.minParticles, particleExplisonSettings.maxParticles),
                                explosionColorsTexture, particleSettings, explosionEffect));

                            models.RemoveAt(j);
                            shots.RemoveAt(j);
                            --i;
                            ((Game1)Game).PlayCue("Explosions");
                            break;
                        }
                    }
                }

            }
        }

        protected void UpdateExplosions(GameTime gameTime)
        {
            for (int i = 0; i < explosions.Count; ++i)
            {
                explosions[i].Update(gameTime);

                if (explosions[i].IsDead)
                {
                    explosions.RemoveAt(i);
                    --i;
                }
            }
        }

                
    }
}
